/*
Open Tracker License

Terms and Conditions

Copyright (c) 1991-2000, Be Incorporated. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.

Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#ifndef _POSE_VIEW_H
#define _POSE_VIEW_H


// BPoseView is a container for poses, handling all of the interaction, drawing,
// etc. The three different view modes are handled here.
//
// this is by far the fattest Tracker class and over time will undergo a lot of
// trimming


#include "AttributeStream.h"
#include "ContainerWindow.h"
#include "Model.h"
#include "PendingNodeMonitorCache.h"
#include "PoseList.h"
#include "TitleView.h"
#include "Utilities.h"
#include "ViewState.h"

#include <ColorConversion.h>
#include <Directory.h>
#include <FilePanel.h>
#include <HashSet.h>
#include <MessageRunner.h>
#include <String.h>
#include <ScrollBar.h>
#include <View.h>
#include <set>


class BRefFilter;
class BList;

namespace BPrivate {

class BCountView;
class BContainerWindow;
class EntryListBase;
class TScrollBar;


const uint32 kMiniIconMode = 'Tmic';
const uint32 kIconMode = 'Ticn';
const uint32 kListMode = 'Tlst';

const uint32 kCheckTypeahead = 'Tcty';

class BPoseView : public BView {
public:
	BPoseView(Model*, uint32 viewMode);
	virtual ~BPoseView();

	// setup, teardown
	virtual void Init(AttributeStreamNode*);
	virtual void Init(const BMessage&);
	void InitCommon();
	virtual void DetachedFromWindow();

	// Returns true if for instance, node ref is a remote desktop
	// directory and this is a desktop pose view.
	virtual bool Represents(const node_ref*) const;
	virtual bool Represents(const entry_ref*) const;

	BContainerWindow* ContainerWindow() const;
	const char* ViewStateAttributeName() const;
	const char* ForeignViewStateAttributeName() const;
	Model* TargetModel() const;

	virtual bool IsFilePanel() const;
	bool IsDesktopWindow() const;
	virtual bool IsDesktopView() const;

	// state saving/restoring
	virtual void SaveState(AttributeStreamNode* node);
	virtual void RestoreState(AttributeStreamNode*);
	virtual void RestoreColumnState(AttributeStreamNode*);
	void AddColumnList(BObjectList<BColumn>*list);
	virtual void SaveColumnState(AttributeStreamNode*);
	virtual void SavePoseLocations(BRect* frameIfDesktop = NULL);
	void DisableSaveLocation();

	virtual void SaveState(BMessage&) const;
	virtual void RestoreState(const BMessage&);
	virtual void RestoreColumnState(const BMessage&);
	virtual void SaveColumnState(BMessage&) const;

	bool StateNeedsSaving();

	// switch between mini icon mode, icon mode and list mode
	virtual void SetViewMode(uint32 mode);
	uint32 ViewMode() const;

	// re-use the pose view for a new directory
 	virtual void SwitchDir(const entry_ref*,
 		AttributeStreamNode* node = NULL);

	// in the rare cases where a pose view needs to be explicitly
	// refreshed (for instance in a query window with a dynamic
	// date query), this is used
	virtual void Refresh();

	// callbacks
	virtual void MessageReceived(BMessage* message);
	virtual void AttachedToWindow();
	virtual void WindowActivated(bool active);
	virtual void MakeFocus(bool = true);
	virtual	BSize MinSize();
	virtual void Draw(BRect update_rect);
	virtual void DrawAfterChildren(BRect update_rect);
	virtual void MouseMoved(BPoint, uint32, const BMessage*);
	virtual void MouseDown(BPoint where);
	virtual void MouseUp(BPoint where);
	virtual void MouseDragged(const BMessage*);
	virtual void MouseLongDown(const BMessage*);
	virtual void MouseIdle(const BMessage*);
	virtual void KeyDown(const char*, int32);
	virtual void Pulse();
	virtual void ScrollTo(BPoint);

	// misc. mode setters
	void SetMultipleSelection(bool);
	void SetDragEnabled(bool);
	void SetDropEnabled(bool);
	void SetSelectionRectEnabled(bool);
	void SetAlwaysAutoPlace(bool);
	void SetSelectionChangedHook(bool);
	void SetEnsurePosesVisible(bool);
	void SetIconMapping(bool);
	void SetAutoScroll(bool);
	void SetPoseEditing(bool);

	void UpdateIcon(BPose* pose);

	// file change notification handler
	virtual bool FSNotification(const BMessage*);

	// scrollbars
	virtual void UpdateScrollRange();
	virtual void SetScrollBarsTo(BPoint);
	virtual void AddScrollBars();
	BScrollBar* HScrollBar() const;
	BScrollBar* VScrollBar() const ;
	BCountView* CountView() const;
	BTitleView* TitleView() const;
	void DisableScrollBars();
	void EnableScrollBars();

	// sorting
	virtual void SortPoses();
	void SetPrimarySort(uint32 attrHash);
	void SetSecondarySort(uint32 attrHash);
	void SetReverseSort(bool reverse);
	uint32 PrimarySort() const;
	uint32 PrimarySortType() const;
	uint32 SecondarySort() const;
	uint32 SecondarySortType() const;
	bool ReverseSort() const;
	void CheckPoseSortOrder(BPose*, int32 index);
	void CheckPoseVisibility(BRect* = NULL);
		// make sure pose fits the screen and/or window bounds if needed

	// view metrics
	font_height FontInfo() const;
		// returns height, descent, etc.
	float FontHeight() const;
	float ListElemHeight() const;
	float ListOffset() const;

	void SetIconPoseHeight();
	float IconPoseHeight() const;
	uint32 UnscaledIconSizeInt() const;
	uint32 IconSizeInt() const;
	BSize IconSize() const;

	BRect Extent() const;
	void GetLayoutInfo(uint32 viewMode, BPoint* grid,
		BPoint* offset) const;

	int32 CountItems() const;
	void UpdateCount();

	rgb_color DeskTextColor() const;
	rgb_color DeskTextBackColor() const;

	rgb_color TextColor(bool selected = false) const;
	rgb_color BackColor(bool selected = false) const;

	bool WidgetTextOutline() const;
	void SetWidgetTextOutline(bool);
		// used to not erase when we have a background image and
		// invalidate instead

	void ScrollView(int32 type);

	// column handling
	void ColumnRedraw(BRect updateRect);
	bool AddColumn(BColumn*, const BColumn* after = NULL);
	bool RemoveColumn(BColumn* column, bool runAlert);
	void MoveColumnTo(BColumn* src, BColumn* dest);
	bool ResizeColumnToWidest(BColumn* column);
	BPoint ResizeColumn(BColumn*, float, float* lastLineDrawPos = NULL,
		void (*drawLineFunc)(BPoseView*, BPoint, BPoint) = 0,
		void (*undrawLineFunc)(BPoseView*, BPoint, BPoint) = 0);
		// returns the bottom right of the last pose drawn or
		// the bottom right of bounds

	BColumn* ColumnAt(int32 index) const;
	BColumn* ColumnFor(uint32 attribute_hash) const;
	BColumn* FirstColumn() const;
	BColumn* LastColumn() const;
	int32 IndexOfColumn(const BColumn*) const;
	int32 CountColumns() const;

	// Where to start the first column
	float StartOffset() const;

	// pose access
	int32 IndexOfPose(const BPose*) const;
	BPose* PoseAtIndex(int32 index) const;

	BPose* FindPose(BPoint where, int32* index = NULL) const;
		// return pose at location h, v (search list starting from
		// bottom so drawing and hit detection reflect the same pose
		// ordering)
	BPose* FindPose(const Model*, int32* index = NULL) const;
	BPose* FindPose(const node_ref*, int32* index = NULL) const;
	BPose* FindPose(const entry_ref*, int32* index = NULL) const;
	BPose* FindPose(const entry_ref*, int32 specifierForm,
		int32* index) const;
		// special form of FindPose used for scripting,
		// <specifierForm> may ask for previous or next pose
	BPose* DeepFindPose(const node_ref* node, int32* index = NULL) const;
		// same as FindPose, node can be a target of the actual
		// pose if the pose is a symlink
	BPose* FirstVisiblePose(int32* _index = NULL) const;
	BPose* LastVisiblePose(int32* _index = NULL) const;

	void OpenInfoWindows();
	void SetDefaultPrinter();

	void IdentifySelection(bool force = false);
	void UnmountSelectedVolumes();
	virtual void OpenParent();

	virtual void OpenSelection(BPose* clicked_pose = NULL,
		int32* index = NULL);
	void OpenSelectionUsing(BPose* clicked_pose = NULL,
		int32* index = NULL);
		// launches the open with window
	virtual void MoveSelectionTo(BPoint, BPoint, BContainerWindow*);
	void DuplicateSelection(BPoint* dropStart = NULL,
		BPoint* dropEnd = NULL);

	// Move to trash calls try to select the next pose in the view
	// when they are dones
	virtual void MoveSelectionToTrash(bool selectNext = true);
	virtual void DeleteSelection(bool selectNext = true, bool confirm = true);
	virtual void MoveEntryToTrash(const entry_ref*,
		bool selectNext = true);

	void RestoreSelectionFromTrash(bool selectNext = true);

	// selection
	PoseList* SelectionList() const;
	void SelectAll();
	void InvertSelection();
	int32 SelectMatchingEntries(const BMessage*);
	void ShowSelectionWindow();
	void ClearSelection();
	void ShowSelection(bool);
	void AddRemovePoseFromSelection(BPose* pose, int32 index,
		bool select);
	int32 CountSelected() const;
	bool SelectedVolumeIsReadOnly() const;
	bool TargetVolumeIsReadOnly() const;
	bool CanEditName() const;
	bool CanMoveToTrashOrDuplicate() const;

	void SetSelectionHandler(BLooper* looper);

	BObjectList<BString>*MimeTypesInSelection();

	// pose selection
	void SelectPose(BPose*, int32 index, bool scrollIntoView = true);
	void AddPoseToSelection(BPose*, int32 index,
		bool scrollIntoView = true);
	void RemovePoseFromSelection(BPose*);
	void SelectPoseAtLocation(BPoint);
	void SelectPoses(int32 start, int32 end);
	void MoveOrChangePoseSelection(int32 to);

	// pose handling
	void ScrollIntoView(BPose* pose, int32 index);
	void ScrollIntoView(BRect poseRect);
	void SetActivePose(BPose*);
	BPose* ActivePose() const;
	void CommitActivePose(bool saveChanges = true);
	static bool PoseVisible(const Model*, const PoseInfo*);
	bool FrameForPose(BPose* targetPose, bool convert, BRect* poseRect);
	bool CreateSymlinkPoseTarget(Model* symlink);
		// used to complete a symlink pose; returns true if
		// target symlink should not be shown
	void ResetPosePlacementHint();
	void PlaceFolder(const entry_ref*, const BMessage*);

	// clipboard handling for poses
	inline bool HasPosesInClipboard();
	inline void SetHasPosesInClipboard(bool hasPoses);
	void SetPosesClipboardMode(uint32 clipboardMode);
	void UpdatePosesClipboardModeFromClipboard(
		BMessage* clipboardReport = NULL);

	// filtering
	void SetRefFilter(BRefFilter*);
	BRefFilter* RefFilter() const;

	// access for mime types represented in the pose view
	void AddMimeType(const char* mimeType);
	const char* MimeTypeAt(int32 index);
	int32 CountMimeTypes();
	void RefreshMimeTypeList();

	// drag&drop handling
	virtual bool HandleMessageDropped(BMessage*);
	static bool HandleDropCommon(BMessage* dragMessage, Model* target,
		BPose*, BView* view, BPoint dropPoint);
		// used by pose views and info windows
	static bool CanHandleDragSelection(const Model* target,
		const BMessage* dragMessage, bool ignoreTypes);
	virtual void DragSelectedPoses(const BPose* clickedPose, BPoint);

	void MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
		bool forceCopy, bool forceMove = false, bool createLink = false,
		bool relativeLink = false);
	static void MoveSelectionInto(Model* destFolder,
		BContainerWindow* srcWindow, BContainerWindow* destWindow,
		uint32 buttons, BPoint loc, bool forceCopy,
		bool forceMove = false, bool createLink = false,
		bool relativeLink = false, BPoint clickPoint = BPoint(0, 0),
		bool pinToGrid = false);

	bool UpdateDropTarget(BPoint, const BMessage*,
		bool trackingContextMenu);
		// return true if drop target changed
	void HiliteDropTarget(bool hiliteState);

	void DragStop();
		// throw away cached up structures

	static bool MenuTrackingHook(BMenu* menu, void* castToThis);
		// hook for spring loaded nav-menus

	// scripting
	virtual BHandler* ResolveSpecifier(BMessage* message, int32 index,
		BMessage* specifier, int32 form, const char* property);
	virtual status_t GetSupportedSuites(BMessage*);

	// string width calls that use local width caches, faster than using
	// the general purpose BView::StringWidth
	float StringWidth(const char*) const;
	float StringWidth(const char*, int32) const;
		// deliberately hide the BView StringWidth here - this makes it
		// easy to have the right StringWidth picked up by
		// template instantiation, as used by WidgetAttributeText

	// show/hide barberpole while a background task is filling
	// up the view, etc.
	void ShowBarberPole();
	void HideBarberPole();

	bool fShowSelectionWhenInactive;
	bool fIsDrawingSelectionRect;

	bool IsWatchingDateFormatChange();
	void StartWatchDateFormatChange();
	void StopWatchDateFormatChange();

	// type ahead filtering
	bool IsFiltering() const;

	void UpdateDateColumns(BMessage*);
	virtual void AdaptToVolumeChange(BMessage*);
	virtual void AdaptToDesktopIntegrationChange(BMessage*);

	void SetTextWidgetToCheck(BTextWidget*, BTextWidget* = NULL);

	BTextWidget* ActiveTextWidget() { return fActiveTextWidget; };
	void SetActiveTextWidget(BTextWidget* w) { fActiveTextWidget = w; };

protected:
	// view setup
	virtual void SetupDefaultColumnsIfNeeded();

	virtual EntryListBase* InitDirentIterator(const entry_ref*);
		// sets up an entry iterator for _add_poses_
		// overriden by QueryPoseView, etc. to provide different iteration
	virtual void ReturnDirentIterator(EntryListBase* iterator);
		// returns the entry iterator after _add_poses_ is done

	void Cleanup(bool doAll = false);
		// clean up poses
	void NewFolder(const BMessage*);
		// create a new folder, optionally specify a location

	void NewFileFromTemplate(const BMessage*);
		// create a new file based on a template, optionally specify
		// a location

	void ShowContextMenu(BPoint);

	// scripting handlers
	virtual bool HandleScriptingMessage(BMessage* message);
	bool SetProperty(BMessage* message, BMessage* specifier, int32 form,
		const char* property, BMessage* reply);
	bool GetProperty(BMessage*, int32, const char*, BMessage*);
	bool CreateProperty(BMessage* message, BMessage* specifier, int32,
		const char*, BMessage* reply);
	bool ExecuteProperty(BMessage* specifier, int32, const char*,
		BMessage* reply);
	bool CountProperty(BMessage*, int32, const char*, BMessage*);
	bool DeleteProperty(BMessage*, int32, const char*, BMessage*);

	void ClearPoses();
		// remove all the current poses from the view

	// pose info read/write calls
	void ReadPoseInfo(Model*, PoseInfo*);
	ExtendedPoseInfo* ReadExtendedPoseInfo(Model*);

	void _CheckPoseSortOrder(PoseList* list, BPose*, int32 index);

	// pose creation
	BPose* EntryCreated(const node_ref*, const node_ref*, const char*,
		int32* index = 0);

	void AddPoseToList(PoseList* list, bool visibleList,
		bool insertionSort, BPose* pose, BRect&viewBounds,
		float& listViewScrollBy, bool forceDraw, int32* indexPtr = NULL);
	BPose* CreatePose(Model*, PoseInfo*, bool insertionSort = true,
		int32* index = 0, BRect* boundsPointer = 0, bool forceDraw = true);
	virtual void CreatePoses(Model**models, PoseInfo* poseInfoArray,
		int32 count, BPose** resultingPoses, bool insertionSort = true,
		int32* lastPoseIndexPointer = 0, BRect* boundsPointer = 0,
		bool forceDraw = false);
	virtual bool ShouldShowPose(const Model*, const PoseInfo*);
		// filter, subclasses override to control which poses show up
		// subclasses should always call inherited
	void CreateVolumePose(BVolume*, bool watchIndividually);

	void CreateTrashPose();

	virtual bool AddPosesThreadValid(const entry_ref*) const;
		// verifies whether or not the current set of AddPoses threads
		// are valid and allowed to be adding poses -- returns false
		// in the case where the directory has been switched while
		// populating the view

	virtual void AddPoses(Model* model = NULL);
		// if <model> is zero, PoseView has other means of iterating
		// through all the entries thaat it adds

	virtual void AddRootPoses(bool watchIndividually, bool mountShared);
		// watchIndividually is used when placing a volume pose onto
		// the Desktop where unlike in the Root window it will not be
		// watched by the folder representing root. If set, each volume
		// will therefore be watched individually
	virtual void RemoveRootPoses();
	virtual void AddTrashPoses();

	virtual bool DeletePose(const node_ref*, BPose* pose = NULL,
		int32 index = 0);
	virtual void DeleteSymLinkPoseTarget(const node_ref* itemNode,
		BPose* pose, int32 index);
		// the pose itself wasn't deleted but it's target node was - the
		// pose must be a symlink
	static void PoseHandleDeviceUnmounted(BPose* pose, Model* model,
		int32 index, BPoseView* poseView, dev_t device);
	static void RemoveNonBootDesktopModels(BPose*, Model* model, int32,
		BPoseView* poseView, dev_t);

	// pose placement
	void CheckAutoPlacedPoses();
		// find poses that need placing and place them in a new spot
	void PlacePose(BPose*, BRect&);
		// find a new place for a pose, starting at fHintLocation
		// and place it
	bool IsValidLocation(const BPose* pose);
	bool IsValidLocation(const BRect& rect);
	status_t GetDeskbarFrame(BRect* frame);
	bool SlotOccupied(BRect poseRect, BRect viewBounds) const;
	void NextSlot(BPose*, BRect&poseRect, BRect viewBounds);
	void TrySettingPoseLocation(BNode* node, BPoint point);
	BPoint PinToGrid(BPoint, BPoint grid, BPoint offset) const;

	// zombie pose handling
	Model* FindZombie(const node_ref*, int32* index = 0);
	BPose* ConvertZombieToPose(Model* zombie, int32 index);

	// pose handling
	BRect CalcPoseRect(const BPose*, int32 index,
		bool firstColumnOnly = false) const;
	BRect CalcPoseRectIcon(const BPose*) const;
	BRect CalcPoseRectList(const BPose*, int32 index,
		bool firstColumnOnly = false) const;
	void DrawPose(BPose*, int32 index, bool fullDraw = true);
	void DrawViewCommon(const BRect&updateRect);

	// pose list handling
	int32 BSearchList(PoseList* poseList, const BPose*, int32* index,
		int32 oldIndex);
	void InsertPoseAfter(BPose* pose, int32* index, int32 orientation,
		BRect* invalidRect);
		// does a CopyBits to scroll poses making room for a new pose,
		// returns rectangle that needs invalidating
	void CloseGapInList(BRect* invalidRect);
	int32 FirstIndexAtOrBelow(int32 y, bool constrainIndex = true) const;
	void AddToVSList(BPose*);
	int32 RemoveFromVSList(const BPose*);
	BPose* FindNearbyPose(char arrow, int32* index);
	BPose* FindBestMatch(int32* index);
	BPose* FindNextMatch(int32* index, bool reverse = false);

	// node monitoring calls
	virtual void StartWatching();
	virtual void StopWatching();

	status_t WatchNewNode(const node_ref* item);
		// the above would ideally be the only call of these three and
		// it would be a virtual, overriding the specific watch mask in
		// query pose view, etc. however we need to call WatchNewNode
		// from inside AddPosesTask while the window is unlocked - we
		// have to use the static and a cached messenger and masks.
	static status_t WatchNewNode(const node_ref*, uint32, BMessenger);
	virtual uint32 WatchNewNodeMask();
		// override to change different watch modes for query pose
		// view, etc.

	// drag&drop handling
	static bool EachItemInDraggedSelection(const BMessage* message,
		bool (*)(BPose*, BPoseView*, void*), BPoseView* poseView,
		void* = NULL);
		// iterates through each pose in current selectiond in the source
		// window of the current drag message; locks the window
		// add const version
	BRect GetDragRect(int32 clickedPoseIndex);
	BBitmap* MakeDragBitmap(BRect dragRect, BPoint clickedPoint,
		int32 clickedPoseIndex, BPoint&offset);
	static bool FindDragNDropAction(const BMessage* dragMessage,
		bool&canCopy, bool&canMove, bool&canLink, bool&canErase);

	static bool CanTrashForeignDrag(const Model*);
	static bool CanCopyOrMoveForeignDrag(const Model*, const BMessage*);
	static bool DragSelectionContains(const BPose* target,
		const BMessage* dragMessage);
	static status_t CreateClippingFile(BPoseView* poseView, BFile&result,
		char* resultingName, BDirectory* directory, BMessage* message,
		const char* fallbackName, bool setLocation = false,
		BPoint dropPoint = BPoint(0, 0));

	// opening files, lanunching
	void OpenSelectionCommon(BPose*, int32*, bool);
		// used by OpenSelection and OpenSelectionUsing
	static void LaunchAppWithSelection(Model*, const BMessage*,
		bool checkTypes = true);

	// node monitoring calls
	virtual bool EntryMoved(const BMessage*);
	virtual bool AttributeChanged(const BMessage*);
	virtual bool NoticeMetaMimeChanged(const BMessage*);
	virtual void MetaMimeChanged(const char*, const char*);

	// click handling
	bool WasDoubleClick(const BPose*, BPoint point, int32 buttons);
	bool WasClickInPath(const BPose*, int32 index,
		BPoint mouseLocation) const;

	// selection
	void SelectPoses(BRect, BList**);
	void AddRemoveSelectionRange(BPoint where, bool extendSelection,
		BPose* pose);

	void _BeginSelectionRect(const BPoint& point, bool extendSelection);
	void _UpdateSelectionRect(const BPoint& point);
	void _EndSelectionRect();

	// view drawing
	void SynchronousUpdate(BRect, bool clip = false);

	// scrolling
	void HandleAutoScroll();
	bool CheckAutoScroll(BPoint mouseLoc, bool shouldScroll);

	// view extent handling
	void RecalcExtent();
	void AddToExtent(const BRect&);
	void ClearExtent();
	void RemoveFromExtent(const BRect&);

	virtual void EditQueries();

	void HandleAttrMenuItemSelected(BMessage*);
	void TryUpdatingBrokenLinks();
		// ran a little after a volume gets mounted

	void MapToNewIconMode(BPose*, BPoint oldGrid, BPoint oldOffset);
	void ResetOrigin();
	void PinPointToValidRange(BPoint&);
		// used to ensure pose locations make sense after getting them
		// in pose info from attributes, etc.

	void FinishPendingScroll(float&listViewScrollBy, BRect bounds);
		// utility call for CreatePoses

	// background AddPoses task calls
	static status_t AddPosesTask(void*);
	virtual void AddPosesCompleted();
	bool IsValidAddPosesThread(thread_id) const;

	// typeahead filtering
	void EnsurePoseUnselected(BPose* pose);
	void RemoveFilteredPose(BPose* pose, int32 index);
	void FilterChanged();
	void UpdateAfterFilterChange();
	bool FilterPose(BPose* pose);
	void StartFiltering();
	void StopFiltering();
	void ClearFilter();
	PoseList* CurrentPoseList() const;

	// misc
	BList* GetDropPointList(BPoint dropPoint, BPoint startPoint,
		const PoseList*, bool sourceInListMode, bool dropOnGrid) const;
	void SendSelectionAsRefs(uint32 what, bool onlyQueries = false);
	void MoveListToTrash(BObjectList<entry_ref>*, bool selectNext,
		bool deleteDirectly);
	void Delete(BObjectList<entry_ref>*, bool selectNext, bool confirm);
	void Delete(const entry_ref&ref, bool selectNext, bool confirm);
	void RestoreItemsFromTrash(BObjectList<entry_ref>*, bool selectNext);
	void DoDelete();
	void DoMoveToTrash();

	void WatchParentOf(const entry_ref*);
	void StopWatchingParentsOf(const entry_ref*);

	void ExcludeTrashFromSelection();

private:
	void DrawOpenAnimation(BRect);
	void ApplyBackgroundColor();
	rgb_color InvertedBackColor() const;

	void MoveSelectionOrEntryToTrash(const entry_ref* ref, bool selectNext);

protected:
	struct node_ref_key {
		node_ref_key() {}
		node_ref_key(const node_ref& value) : value(value) {}

		uint32 GetHashCode() const
		{
			return (uint32)value.device ^ (uint32)value.node;
		}

		node_ref_key operator=(const node_ref_key& other)
		{
			value = other.value;
			return *this;
		}

		bool operator==(const node_ref_key& other) const
		{
			return (value == other.value);
		}

		bool operator!=(const node_ref_key& other) const
		{
			return (value != other.value);
		}

		node_ref	value;
	};

protected:
	BViewState* fViewState;
	bool fStateNeedsSaving;

	bool fSavePoseLocations : 1;
	bool fMultipleSelection : 1;
	bool fDragEnabled : 1;
	bool fDropEnabled : 1;

	BLooper* fSelectionHandler;

	std::set<thread_id> fAddPosesThreads;
	PoseList* fPoseList;

	PendingNodeMonitorCache pendingNodeMonitorCache;

private:
	TScrollBar* fHScrollBar;
	BScrollBar* fVScrollBar;
	Model* fModel;
	BPose* fActivePose;
	BRect fExtent;
	// the following should probably be just member lists, not pointers
	PoseList* fFilteredPoseList;
	PoseList* fVSPoseList;
	PoseList* fSelectionList;
	HashSet<node_ref_key> fInsertedNodes;
	BObjectList<BString> fMimeTypesInSelectionCache;
		// used for mime string based icon highliting during a drag
	BObjectList<Model>* fZombieList;
	BObjectList<BColumn>* fColumnList;
	BObjectList<BString>* fMimeTypeList;
	BObjectList<Model>* fBrokenLinks;
	bool fMimeTypeListIsDirty;
	BCountView* fCountView;
	float fListElemHeight;
	float fListOffset;
	float fIconPoseHeight;
	BPose* fDropTarget;
	BPose* fAlreadySelectedDropTarget;
	BPoint fLastClickPoint;
	int32 fLastClickButtons;
	const BPose* fLastClickedPose;
	BPoint fLastLeftTop;
	BRect fLastExtent;
	BTitleView* fTitleView;
	BRefFilter* fRefFilter;
	BPoint fGrid;
	BPoint fOffset;
	BPoint fHintLocation;
	float fAutoScrollInc;
	int32 fAutoScrollState;
	bool fWidgetTextOutline;
	const BPose* fSelectionPivotPose;
	const BPose* fRealPivotPose;
	BMessageRunner* fKeyRunner;
	bool fTrackRightMouseUp;
	bool fTrackMouseUp;

	struct SelectionRectInfo {
		SelectionRectInfo()
			:
			isDragging(false),
			selection(NULL)
		{
		}

		bool isDragging;
		BRect rect;
		BRect lastRect;
		BPoint startPoint;
		BPoint lastPoint;
		BList* selection;
	};
	SelectionRectInfo fSelectionRectInfo;

	bool fSelectionVisible : 1;
	bool fSelectionRectEnabled : 1;
	bool fTransparentSelection : 1;
	bool fAlwaysAutoPlace : 1;
	bool fAllowPoseEditing : 1;
	bool fSelectionChangedHook : 1;
		// get rid of this
	bool fOkToMapIcons : 1;
	bool fEnsurePosesVisible : 1;
	bool fShouldAutoScroll : 1;
	bool fIsDesktopWindow : 1;
	bool fIsWatchingDateFormatChange : 1;
	bool fHasPosesInClipboard : 1;
	bool fCursorCheck : 1;
	bool fFiltering : 1;

	BObjectList<BString> fFilterStrings;
	int32 fLastFilterStringCount;
	int32 fLastFilterStringLength;

	BRect fStartFrame;

	static float sFontHeight;
	static font_height sFontInfo;
	static BString sMatchString;
		// used for typeahead - should be replaced by a typeahead state

	bigtime_t fLastKeyTime;
	bigtime_t fLastDeskbarFrameCheckTime;
	BRect fDeskbarFrame;

	static OffscreenBitmap* sOffscreen;

	BTextWidget* fTextWidgetToCheck;
	BTextWidget* fActiveTextWidget;

private:
	mutable uint32 fCachedIconSizeFrom;
	mutable BSize fCachedIconSize;

	typedef BView _inherited;
};


class TScrollBar : public BScrollBar {
public:
	TScrollBar(const char*, BView*, float, float);
	void SetTitleView(BView*);

	// BScrollBar overrides
	virtual	void ValueChanged(float);

private:
	BView* fTitleView;

	typedef BScrollBar _inherited;
};


class TPoseViewFilter : public BMessageFilter {
public:
	TPoseViewFilter(BPoseView* pose);
	~TPoseViewFilter();

	filter_result Filter(BMessage*, BHandler**);

private:
	filter_result ObjectDropFilter(BMessage*, BHandler**);

	BPoseView* fPoseView;
};


extern bool
ClearViewOriginOne(const char* name, uint32 type, off_t size, void* data,
	void* params);


// inlines follow


inline BContainerWindow*
BPoseView::ContainerWindow() const
{
	return dynamic_cast<BContainerWindow*>(Window());
}


inline Model*
BPoseView::TargetModel() const
{
	return fModel;
}


inline float
BPoseView::ListElemHeight() const
{
	return fListElemHeight;
}


inline float
BPoseView::ListOffset() const
{
	return fListOffset;
}


inline float
BPoseView::IconPoseHeight() const
{
	return fIconPoseHeight;
}


inline uint32
BPoseView::UnscaledIconSizeInt() const
{
	return fViewState->IconSize();
}


inline uint32
BPoseView::IconSizeInt() const
{
	return IconSize().IntegerWidth() + 1;
}


inline PoseList*
BPoseView::SelectionList() const
{
	return fSelectionList;
}

inline int32
BPoseView::CountSelected() const
{
	return fSelectionList->CountItems();
}

inline BObjectList<BString>*
BPoseView::MimeTypesInSelection()
{
	return &fMimeTypesInSelectionCache;
}


inline BScrollBar*
BPoseView::HScrollBar() const
{
	return fHScrollBar;
}


inline BScrollBar*
BPoseView::VScrollBar() const
{
	return fVScrollBar;
}


inline BCountView*
BPoseView::CountView() const
{
	return fCountView;
}


inline BTitleView*
BPoseView::TitleView() const
{
	return fTitleView;
}


inline bool
BPoseView::StateNeedsSaving()
{
	return fStateNeedsSaving || fViewState->StateNeedsSaving();
}


inline uint32
BPoseView::ViewMode() const
{
	return fViewState->ViewMode();
}


inline font_height
BPoseView::FontInfo() const
{
	return sFontInfo;
}


inline float
BPoseView::FontHeight() const
{
	return sFontHeight;
}


inline BPose*
BPoseView::ActivePose() const
{
	return fActivePose;
}


inline void
BPoseView::DisableSaveLocation()
{
	fSavePoseLocations = false;
}


inline bool
BPoseView::IsFilePanel() const
{
	return false;
}


inline bool
BPoseView::IsDesktopWindow() const
{
	return fIsDesktopWindow;
}


inline bool
BPoseView::IsDesktopView() const
{
	return false;
}


inline rgb_color
BPoseView::DeskTextColor() const
{
	// The desktop color is chosen independently for the desktop.
	// The text color is chosen globally for all directories.
	// It's fairly easy to get something unreadable (even with the default
	// settings, it's expected that text will be black on white in Tracker
	// folders, but white on blue on the desktop).
	// So here we check if the colors are different enough, and otherwise,
	// force the text to be either white or black.
	rgb_color textColor = HighColor();
	rgb_color viewColor = ViewColor();

	// The colors are different enough, we can use them as is
	if (rgb_color::Contrast(viewColor, textColor) > 127)
		return textColor;

	return viewColor.IsLight() ? kBlack : kWhite;
}


inline rgb_color
BPoseView::DeskTextBackColor() const
{
	// returns black or white color depending on the desktop background
	int32 thresh = 0;
	rgb_color color = LowColor();

	if (color.red > 150)
		thresh++;

	if (color.green > 150)
		thresh++;

	if (color.blue > 150)
		thresh++;

	if (thresh > 1) {
		color.red = 255;
		color.green = 255;
		color.blue = 255;
	} else {
		color.red = 0;
		color.green = 0;
		color.blue = 0;
	}

	return color;
}


inline uint32
BPoseView::PrimarySort() const
{
	return fViewState->PrimarySort();
}


inline uint32
BPoseView::PrimarySortType() const
{
	return fViewState->PrimarySortType();
}


inline uint32
BPoseView::SecondarySort() const
{
	return fViewState->SecondarySort();
}


inline uint32
BPoseView::SecondarySortType() const
{
	return fViewState->SecondarySortType();
}


inline bool
BPoseView::ReverseSort() const
{
	return fViewState->ReverseSort();
}


inline void
BPoseView::SetIconMapping(bool on)
{
	fOkToMapIcons = on;
}


inline void
BPoseView::AddToExtent(const BRect&rect)
{
	fExtent = fExtent | rect;
}


inline void
BPoseView::ClearExtent()
{
	fExtent.Set(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN);
}


inline int32
BPoseView::CountColumns() const
{
	return fColumnList->CountItems();
}


inline float
BPoseView::StartOffset() const
{
	return fListOffset + ListIconSize() + kMiniIconSeparator + 1;
}


inline int32
BPoseView::IndexOfColumn(const BColumn* column) const
{
	return fColumnList->IndexOf(const_cast<BColumn*>(column));
}


inline int32
BPoseView::IndexOfPose(const BPose* pose) const
{
	return CurrentPoseList()->IndexOf(pose);
}


inline BPose*
BPoseView::PoseAtIndex(int32 index) const
{
	return CurrentPoseList()->ItemAt(index);
}


inline BColumn*
BPoseView::ColumnAt(int32 index) const
{
	return fColumnList->ItemAt(index);
}


inline BColumn*
BPoseView::FirstColumn() const
{
	return fColumnList->FirstItem();
}


inline BColumn*
BPoseView::LastColumn() const
{
	return fColumnList->LastItem();
}


inline int32
BPoseView::CountItems() const
{
	return CurrentPoseList()->CountItems();
}


inline void
BPoseView::SetMultipleSelection(bool state)
{
	fMultipleSelection = state;
}


inline void
BPoseView::SetSelectionChangedHook(bool state)
{
	fSelectionChangedHook = state;
}


inline void
BPoseView::SetAutoScroll(bool state)
{
	fShouldAutoScroll = state;
}


inline void
BPoseView::SetPoseEditing(bool state)
{
	fAllowPoseEditing = state;
}


inline void
BPoseView::SetDragEnabled(bool state)
{
	fDragEnabled = state;
}


inline void
BPoseView::SetDropEnabled(bool state)
{
	fDropEnabled = state;
}


inline void
BPoseView::SetSelectionRectEnabled(bool state)
{
	fSelectionRectEnabled = state;
}


inline void
BPoseView::SetAlwaysAutoPlace(bool state)
{
	fAlwaysAutoPlace = state;
}


inline void
BPoseView::SetEnsurePosesVisible(bool state)
{
	fEnsurePosesVisible = state;
}


inline void
BPoseView::SetSelectionHandler(BLooper* looper)
{
	fSelectionHandler = looper;
}


inline void
TScrollBar::SetTitleView(BView* view)
{
	fTitleView = view;
}


inline void
BPoseView::SetRefFilter(BRefFilter* filter)
{
	fRefFilter = filter;
	if (filter != NULL)
		FilterChanged();
}


inline BRefFilter*
BPoseView::RefFilter() const
{
	return fRefFilter;
}


inline BPose*
BPoseView::FindPose(const Model* model, int32* index) const
{
	return CurrentPoseList()->FindPose(model, index);
}


inline BPose*
BPoseView::FindPose(const node_ref* node, int32* index) const
{
	return CurrentPoseList()->FindPose(node, index);
}


inline BPose*
BPoseView::FindPose(const entry_ref* entry, int32* index) const
{
	return CurrentPoseList()->FindPose(entry, index);
}


inline bool
BPoseView::HasPosesInClipboard()
{
	return fHasPosesInClipboard;
}


inline void
BPoseView::SetHasPosesInClipboard(bool hasPoses)
{
	fHasPosesInClipboard = hasPoses;
}


inline PoseList*
BPoseView::CurrentPoseList() const
{
	return fFiltering ? fFilteredPoseList : fPoseList;
}


template<class Param1>
void
EachTextWidget(BPose* pose, BPoseView* poseView,
	void (*func)(BTextWidget*, BPose*, BPoseView*, BColumn*, Param1), Param1 p1)
{
	for (int32 index = 0; ;index++) {
		BColumn* column = poseView->ColumnAt(index);
		if (column == NULL)
			break;

		BTextWidget* widget = pose->WidgetFor(column->AttrHash());
		if (widget != NULL)
			(func)(widget, pose, poseView, column, p1);
	}
}


template<class Param1, class Param2>
void
EachTextWidget(BPose* pose, BPoseView* poseView,
	void (*func)(BTextWidget*, BPose*, BPoseView*, BColumn*,
	Param1, Param2), Param1 p1, Param2 p2)
{
	for (int32 index = 0; ;index++) {
		BColumn* column = poseView->ColumnAt(index);
		if (column == NULL)
			break;

		BTextWidget* widget = pose->WidgetFor(column->AttrHash());
		if (widget != NULL)
			(func)(widget, pose, poseView, column, p1, p2);
	}
}


template<class Result, class Param1, class Param2>
Result
WhileEachTextWidget(BPose* pose, BPoseView* poseView,
	Result (*func)(BTextWidget*, BPose*, BPoseView*, BColumn*,
	Param1, Param2), Param1 p1, Param2 p2)
{
	for (int32 index = 0; ;index++) {
		BColumn* column = poseView->ColumnAt(index);
		if (column == NULL)
			break;

		BTextWidget* widget = pose->WidgetFor(column->AttrHash());
		if (widget != NULL) {
			Result result = (func)(widget, pose, poseView, column, p1, p2);
			if (result != 0)
				return result;
		}
	}

	return 0;
}


} // namespace BPrivate

using namespace BPrivate;


#endif	// _POSE_VIEW_H
